package com.mysteel.ware.service.impl;

import com.mysteel.common.to.mq.OrderTo;
import com.mysteel.common.to.mq.StockDetailTo;
import com.mysteel.common.to.mq.StockLockedTo;
import com.mysteel.common.utils.R;
import com.mysteel.ware.entity.WareOrderTaskDetailEntity;
import com.mysteel.ware.entity.WareOrderTaskEntity;
import com.mysteel.ware.exception.NoStockException;
import com.mysteel.ware.feign.OrderFeignService;
import com.mysteel.ware.feign.ProductFeignService;
import com.mysteel.common.to.SkuHasStockTo;
import com.mysteel.ware.service.WareOrderTaskDetailService;
import com.mysteel.ware.service.WareOrderTaskService;
import com.mysteel.ware.vo.LockStockResult;
import com.mysteel.ware.vo.OrderItemVo;
import com.mysteel.ware.vo.OrderVo;
import com.mysteel.ware.vo.WareSkuLockVo;
import com.rabbitmq.client.Channel;
import com.sun.org.apache.bcel.internal.generic.NEW;
import lombok.Data;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mysteel.common.utils.PageUtils;
import com.mysteel.common.utils.Query;

import com.mysteel.ware.dao.WareSkuDao;
import com.mysteel.ware.entity.WareSkuEntity;
import com.mysteel.ware.service.WareSkuService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;


@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Resource
    private WareSkuDao wareSkuDao;

    @Autowired
    private ProductFeignService productFeignService;

    @Autowired
    private WareOrderTaskService wareOrderTaskService;

    @Autowired
    private WareOrderTaskDetailService orderTaskDetailService;

    @Autowired
    private OrderFeignService orderFeignService;

    @Override
    public void handleStockLockedRelease(StockLockedTo stockLockedTo) throws Exception {
        StockDetailTo detail = stockLockedTo.getDetail();
        WareOrderTaskDetailEntity byId = orderTaskDetailService.getById(detail.getId());
        if (byId != null){
            Long taskId = stockLockedTo.getTaskId();
            WareOrderTaskEntity wareTask = wareOrderTaskService.getById(taskId);
            String orderSn = wareTask.getOrderSn();
            OrderVo orderByOrderSn = orderFeignService.getOrderByOrderSn(orderSn);
            /**
             * 1、订单已经取消，需要解锁库存
             * 2、订单为空，订单服务发生回滚，需要解锁库存
             */
            if (orderByOrderSn == null || orderByOrderSn.getStatus().equals(4)){
                if (byId.getLockStatus().equals(1)){
                    unlockStock(detail.getSkuId(),detail.getWareId(),detail.getSkuNum(),detail.getId());
                }
            }else {
                throw new Exception("调用远程服务异常");
            }
        }
    }

    /**
     * 解锁库存信息
     * @param skuId
     * @param wareId
     * @param num
     * @param taskDetailId
     */
    private void unlockStock(Long skuId,Long wareId,Integer num,Long taskDetailId){
        wareSkuDao.unlockStock(skuId, wareId, num);
        // 更新库存工作单状态
        WareOrderTaskDetailEntity wareOrderTaskDetailEntity = new WareOrderTaskDetailEntity();
        wareOrderTaskDetailEntity.setId(taskDetailId);
        // status为2：已解锁
        wareOrderTaskDetailEntity.setLockStatus(2);
        orderTaskDetailService.updateById(wareOrderTaskDetailEntity);
    }

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                new QueryWrapper<WareSkuEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public PageUtils queryPageOnCondition(Map<String, Object> params) {
        QueryWrapper<WareSkuEntity> queryWrapper = new QueryWrapper<>();
        String skuId = (String) params.get("skuId");
        String wareId = (String) params.get("wareId");
        if (StringUtils.isNotEmpty(skuId)){
            queryWrapper.eq("sku_id",skuId);
        }
        if (StringUtils.isNotEmpty(wareId)){
            queryWrapper.eq("ware_id",wareId);
        }
        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                queryWrapper
        );

        return new PageUtils(page);
    }

    @Override
    public void addStock(Long skuId, Long wareId, Integer skuNum) {
        // 如果该商品还没库存记录信息，则新增，否则修改
        List<WareSkuEntity> wareSkuEntities = wareSkuDao.selectList(new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId).eq("ware_id", wareId));
        if (CollectionUtils.isEmpty(wareSkuEntities)){
            WareSkuEntity wareSkuEntity = new WareSkuEntity();
            wareSkuEntity.setSkuId(skuId);
            wareSkuEntity.setStock(skuNum);
            wareSkuEntity.setStockLocked(0);
            // 调用远程接口查询skuName，如果失败，事务无需回滚（捕获异常）
            // todo 出现异常无需回滚，可以设置事务的隔离级别
            try {
                R info = productFeignService.info(skuId);
                if (info.getCode() == 0){
                    Map<String,Object> skuInfo = (Map<String, Object>) info.get("skuInfo");
                    String skuName = (String) skuInfo.get("skuName");
                    wareSkuEntity.setSkuName(skuName);
                }

            }catch (Exception e){
                log.error("调用远程接口查询skuName失败...");
            }
            wareSkuEntity.setWareId(wareId);
            wareSkuDao.insert(wareSkuEntity);
        }else {
            wareSkuDao.addStock(skuId,wareId,skuNum);
        }

    }

    @Override
    public List<SkuHasStockTo> getSkuHasStock(List<Long> skuIds) {
        List<WareSkuEntity> skuEntities = this.baseMapper.selectList(new QueryWrapper<WareSkuEntity>().in("sku_id", skuIds));
        List<SkuHasStockTo> collect = skuEntities.stream().map(item -> {
            SkuHasStockTo skuHasStockTo = new SkuHasStockTo();
            skuHasStockTo.setSkuId(item.getSkuId());
            boolean hasStock = true;
            Integer lockStock = item.getStockLocked() == null ? 0 : item.getStockLocked();
            if (item.getStock() - lockStock <= 0){
                hasStock = false;
            }
            skuHasStockTo.setHasStock(hasStock);
            // 查询sku库存量
            return skuHasStockTo;
        }).collect(Collectors.toList());
        // 查找没有录入库存信息的sku，设置其库存量
        List<SkuHasStockTo> notFoundSkuIdStock = skuIds.stream()
                .filter(id -> skuEntities.stream().noneMatch(sku -> sku.getSkuId().equals(id)))
                .map(skuId -> {
                    SkuHasStockTo skuHasStockTo = new SkuHasStockTo();
                    skuHasStockTo.setSkuId(skuId);
                    skuHasStockTo.setHasStock(false);
                    return skuHasStockTo;
                })
                .collect(Collectors.toList());
        collect.addAll(notFoundSkuIdStock);
        return collect;
    }

    /**
     * 默认只要是运行时异常都要回滚
     *
     * 库存解锁场景：
     * 1、订单下单成功，订单过期没有支付被系统自动取消或被用户手动取消
     * 2、下订单成功，接下来业务调用失败，导致订单回滚，之前锁定的库存就需要解锁
     * @param lockVo
     * @return
     */
    @Transactional(rollbackFor = NoStockException.class)
    @Override
    public Boolean lockOrderStock(WareSkuLockVo lockVo) throws NoStockException {
        // 保存库存工作单信息
        WareOrderTaskEntity wareOrderTaskEntity = new WareOrderTaskEntity();
        wareOrderTaskEntity.setOrderSn(lockVo.getOrderSn());
        wareOrderTaskService.save(wareOrderTaskEntity);

        // 找到每个商品在哪个仓库
        List<OrderItemVo> locks = lockVo.getLocks();
        List<SkuWareHasStock> collect = locks.stream().map(orderItemVo -> {
            SkuWareHasStock skuWareHasStock = new SkuWareHasStock();
            skuWareHasStock.setSkuId(orderItemVo.getSkuId());
            skuWareHasStock.setNum(orderItemVo.getCount());
            List<Long> wareIds = this.baseMapper.listWareIdHasSkuStock(orderItemVo.getSkuId());
            skuWareHasStock.setWareIds(wareIds);
            return skuWareHasStock;
        }).collect(Collectors.toList());

        // 锁定库存
        for (SkuWareHasStock hasStock : collect) {
            boolean skuStocked = false;
            Long skuId = hasStock.getSkuId();
            List<Long> wareIds = hasStock.getWareIds();
            if (CollectionUtils.isEmpty(wareIds)){
                throw new NoStockException(skuId);
            }
            for (Long wareId : wareIds) {
                // 影响行数，成功就返回1，否则就算失败
                Long affectRows = wareSkuDao.lockSkuStock(skuId,wareId,hasStock.getNum());
                if (affectRows == 1){
                    // 仓库库存锁成功了，保存库存工作单信息
                    WareOrderTaskDetailEntity taskDetailEntity = new WareOrderTaskDetailEntity();
                    taskDetailEntity.setSkuId(skuId);
                    taskDetailEntity.setSkuNum(hasStock.getNum());
                    taskDetailEntity.setTaskId(wareOrderTaskEntity.getId());
                    taskDetailEntity.setWareId(wareId);
                    taskDetailEntity.setLockStatus(1);
                    orderTaskDetailService.save(taskDetailEntity);
                    // 发送消息至消息队列
                    StockLockedTo stockLockedTo = new StockLockedTo();
                    stockLockedTo.setTaskId(wareOrderTaskEntity.getId());
                    StockDetailTo stockDetailTo = new StockDetailTo();
                    BeanUtils.copyProperties(taskDetailEntity,stockDetailTo);
                    stockLockedTo.setDetail(stockDetailTo);
                    rabbitTemplate.convertAndSend("stock-event-exchange","stock.locked",stockLockedTo);
                    skuStocked = true;
                    break;
                }
            }
            /**
             * 如果锁定失败，前面数据则回滚，消息发送出去了，回滚则找不到记录
             */
            if (!skuStocked){
                // 当前商品所有仓库都数量不够
                throw new NoStockException(skuId);
            }
        }
        return true;
    }

    /**
     * 防止订单系统卡顿导致订单状态一直改变不了，库存消息优先到期，订单状态未变，库存无法解锁
     * @param orderTo
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void unlockStock(OrderTo orderTo) throws Exception {
        String orderSn = orderTo.getOrderSn();
        // 查询库存工作单
        WareOrderTaskEntity orderTask = wareOrderTaskService.getOne(new QueryWrapper<WareOrderTaskEntity>().eq("order_sn", orderSn));
        if (orderTask == null){
            throw new Exception("");
        }
        Long taskId = orderTask.getId();
        // 按照库存工作单找到所有没有解锁的库存进行解锁
        List<WareOrderTaskDetailEntity> taskDetailList = orderTaskDetailService.list(new QueryWrapper<WareOrderTaskDetailEntity>()
                .eq("task_id", taskId)
                .eq("lock_status", 1)
        );
        if (!CollectionUtils.isEmpty(taskDetailList)){
            for (WareOrderTaskDetailEntity orderTaskDetail : taskDetailList) {
                unlockStock(orderTaskDetail.getSkuId(),orderTaskDetail.getWareId(),orderTaskDetail.getSkuNum(),orderTaskDetail.getId());
            }
        }
    }

    @Data
    class SkuWareHasStock{
        private Long skuId;

        private Integer num;
        /**
         * 有sku的仓库集合
         */
        private List<Long> wareIds;
    }
}